#ifndef CLIENT_SIMULATOR_H_
#define CLIENT_SIMULATOR_H_

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>  /* netdb is necessary for struct hostent */
#include <fstream>
#include <sstream>
#include <iostream>
#include "protoc/vector.pb.h"
#include "json/json.h"

#define MAXDATASIZE 1024 * 16
struct CPacketHeader {
    uint16_t magic;
    uint16_t cmd;
    uint32_t len;
    uint32_t seq_num;
};

struct ArgvCnt{
    std::string s_program_dir;
    std::string s_send_filename;
    uint16_t ui_cmd;
    uint16_t ui_topk;
    std::string s_ip;
    uint32_t ui_port;
    std::string s_local_socket;
};

int count = 0;
int main(int argc, char *argv[])
{
    if (argc != 5 && argc != 6) {
        printf("Usage: %d <send file name> <reqcode> <topk> {<ip> <port> , or <local socket>}\n", argc);
        exit(1);
    }
    
    ArgvCnt o_arg_cnt;
    o_arg_cnt.s_program_dir = argv[0];
    o_arg_cnt.s_send_filename = argv[1];
    o_arg_cnt.ui_cmd = htons(atoi(argv[2]));
    o_arg_cnt.ui_topk = atoi(argv[3]);

    if (5 == argc ) {
        o_arg_cnt.s_local_socket = argv[4];
    } else if (6 == argc) {
        o_arg_cnt.s_ip = argv[4];
        o_arg_cnt.ui_port = htons(atoi(argv[5]));
    } else {
        std::cout << "not support" << std::endl;
        exit(1);
    }

    int client_sockfd;    /* files descriptors */
    
    char buf[MAXDATASIZE];    /* buf will store received text */

    if (o_arg_cnt.s_local_socket[0] == '/' ||
        o_arg_cnt.s_local_socket[0] == '@') {
        client_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
        struct sockaddr_un addr;
        addr.sun_family = AF_UNIX;
        strncpy(addr.sun_path, o_arg_cnt.s_local_socket.c_str(), sizeof(addr.sun_path));
        if(connect(client_sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
        {
            printf("connect() error\n");
            exit(1);
        }
    } else {
        struct hostent* he;    /* structure that will get information about remote host */
        if((he = gethostbyname(o_arg_cnt.s_ip.c_str())) == NULL) {
            printf("gethostbyname() error\n");
            exit(1);
        }

        if((client_sockfd = socket(AF_INET,SOCK_STREAM, 0)) == -1)
        {
            printf("socket() error\n");
            exit(1);
        }
        
        struct sockaddr_in server;
        bzero(&server,sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = o_arg_cnt.ui_port;
        server.sin_addr = *((struct in_addr *)he->h_addr);

        struct timeval timeout = {10,0};
        setsockopt(client_sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));
        if(connect(client_sockfd, (struct sockaddr *)&server, sizeof(server))==-1)
        {
            printf("connect() error\n");
            exit(1);
        }
    }
    
    std::ifstream inf;
    inf.open(o_arg_cnt.s_send_filename);
    if (inf.is_open() == false) {
        printf("open file error: %s.\n", "./stop_words.dict");
        return -1;
    }

    CPacketHeader a;
    a.magic = 0xfcfd;
    a.cmd = o_arg_cnt.ui_cmd;
    a.seq_num = 0x3131;

    std::string s,sender;
    int num;
    while (getline(inf, s)) {
        sender = s;

        VectorReq o_vet_req;
        o_vet_req.set_topk(o_arg_cnt.ui_topk);

        Json::Reader reader;
        Json::Value send_json_value;
        if (reader.parse(sender , send_json_value)) {
            if (send_json_value.isMember("appId")
                && send_json_value["appId"].isInt()) {
                o_vet_req.set_appid(send_json_value["appId"].asUInt());
            } else {
                std::cout << "appId is incorrect" << std::endl;
                getchar();
                return -1;
            }

            if (send_json_value.isMember("fieldId")
                && send_json_value["fieldId"].isInt()) {
                o_vet_req.set_field_id(send_json_value["fieldId"].asUInt());
            } else {
                std::cout << "fieldId is incorrect" << std::endl;
                getchar();
                return -1;
            }

            if (send_json_value.isMember("defaultIndexType")
                && send_json_value["defaultIndexType"].isInt()) {
                o_vet_req.set_index_typeid(send_json_value["defaultIndexType"].asUInt());
            } else {
                std::cout << "defaultIndexType is incorrect" << std::endl;
                getchar();
                return -1;
            }

            if (send_json_value.isMember("vectorData")
                && send_json_value["vectorData"].isArray()) {
                    Json::Value o_vector_data_array = send_json_value["vectorData"];
                    for (int i = 0; i < (int)o_vector_data_array.size(); i++) {
                        if (o_vector_data_array[i].isDouble()) {
                            o_vet_req.add_vector_data(o_vector_data_array[i].asDouble());
                        }
                    }
            } else {
                std::cout << "vectorData is incorrect" << std::endl;
                getchar();
                return -1;
            }
        }

        sender.clear();
        bool b_ret = o_vet_req.SerializeToString(&sender);
        if (!b_ret) {
            std::cout << "serial to req has errors" << std::endl;
            getchar();
            return -1;
        }

        a.len = htonl(sender.length());

        printf("a.magic = %x\na.len = %d\n",a.magic , ntohl(a.len));

        if((num = send(client_sockfd,&a,sizeof(a),0))==-1){
            printf("send(head) error\n");
            exit(1);
        }

        if((num = send(client_sockfd,sender.c_str(),sender.length(),0))==-1){
            printf("send(str) error\n");
            exit(1);
        }
        printf("send count == %d \n",++count);

        memset(buf , MAXDATASIZE * sizeof(char) , 0);
        if((num = recv(client_sockfd,buf,MAXDATASIZE,0))==-1)
        {
            printf("recv() error\n");
            s = "";
            continue;
        }

        VectorRsp o_vet_rsp_cnt;
        b_ret = o_vet_rsp_cnt.ParseFromArray(&buf[sizeof(a)] , num - sizeof(a));
        if (!b_ret) {
            printf("parse from array failed");
            return -1;
        }
        printf("recv: code:%d,info:%s", o_vet_rsp_cnt.code(), o_vet_rsp_cnt.info().c_str());
        for (int i = 0; i < (int)o_vet_rsp_cnt.vector_id().size(); i++)
        {
            printf("vecter id: %d \n", (int)o_vet_rsp_cnt.vector_id(i));
        }

        for (int j = 0; j < (int)o_vet_rsp_cnt.vector_dis().size(); j++)
        {
            printf("vecter dis: %4f \n", o_vet_rsp_cnt.vector_dis(j));
        }
        
        s = "";
    }
    std::cout << "before close: "<< s << std::endl;
    inf.close();
    close(client_sockfd);
    return 0;
}
#endif
